/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain (a) copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry.ohos.core;

import ohos.aafwk.ability.Lifecycle;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

import java.io.Closeable;
import java.io.IOException;

import io.sentry.IHub;
import io.sentry.Integration;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.ohos.core.util.MainThreadChecker;
import io.sentry.util.Objects;

/**
 * AppLifecycleIntegration
 *
 * @since 2021-06-01
 */
public final class AppLifecycleIntegration implements Integration, Closeable {
    Lifecycle lifecycle = new Lifecycle();

    /**
     * watcher
     */
    @TestOnly
    @Nullable
    public LifecycleWatcher watcher;

    private @Nullable SentryOhosOptions sentryOhosOptions;

    private final @NotNull IHandler handler;

    /**
     * AppLifecycleIntegration
     *
     * @since 2021-06-01
     */
    public AppLifecycleIntegration() {
        this(new MainLooperHandler());
    }

    public AppLifecycleIntegration(final @NotNull IHandler handler) {
        this.handler = handler;
    }

    /**
     * register
     *
     * @param hub the Hub
     * @param options the options
     */
    @Override
    public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) {
        Objects.requireNonNull(hub, "Hub is required");
        this.sentryOhosOptions =
            Objects.requireNonNull(
                (options instanceof SentryOhosOptions) ? (SentryOhosOptions) options : null,
                "SentryOhosOptions is required");

        this.sentryOhosOptions
            .getLogger()
            .log(
                SentryLevel.DEBUG,
                "enableSessionTracking enabled: %s",
                this.sentryOhosOptions.isEnableAutoSessionTracking());

        this.sentryOhosOptions
            .getLogger()
            .log(
                SentryLevel.DEBUG,
                "enableAppLifecycleBreadcrumbs enabled: %s",
                this.sentryOhosOptions.isEnableAppLifecycleBreadcrumbs());

        if (this.sentryOhosOptions.isEnableAutoSessionTracking()
            || this.sentryOhosOptions.isEnableAppLifecycleBreadcrumbs()) {
            try {
                Class.forName("ohos.lifecycle.DefaultLifecycleObserver");
                Class.forName("ohos.lifecycle.ProcessLifecycleOwner");
                if (MainThreadChecker.isMainThread()) {
                    addObserver(hub);
                } else {
                    // thread.
                    handler.postTask(() -> addObserver(hub));
                }
            } catch (ClassNotFoundException e) {
                options
                    .getLogger()
                    .log(
                        SentryLevel.INFO,
                        "ohos.lifecycle is not available, AppLifecycleIntegration won't be installed",
                        e);
            } catch (IllegalStateException e) {
                options
                    .getLogger()
                    .log(SentryLevel.ERROR, "AppLifecycleIntegration could not be installed", e);
            }
        }
    }

    private void addObserver(final @NotNull IHub hub) {
        // this should never happen, check added to avoid warnings from NullAway
        if (this.sentryOhosOptions == null) {
            return;
        }
        watcher =
            new LifecycleWatcher(
                hub,
                this.sentryOhosOptions.getSessionTrackingIntervalMillis(),
                this.sentryOhosOptions.isEnableAutoSessionTracking(),
                this.sentryOhosOptions.isEnableAppLifecycleBreadcrumbs());

        lifecycle.addObserver(watcher);
        sentryOhosOptions.getLogger().log(SentryLevel.DEBUG, "AppLifecycleIntegration installed.");
    }

    private void removeObserver() {
        lifecycle.removeObserver(watcher);
    }

    /**
     * close
     *
     * @throws IOException
     */
    @Override
    public void close() throws IOException {
        if (watcher != null) {
            if (MainThreadChecker.isMainThread()) {
                removeObserver();
            } else {
                // thread.
                handler.postTask(() -> removeObserver());
            }
            watcher = null;
            if (sentryOhosOptions != null) {
                sentryOhosOptions.getLogger().log(SentryLevel.DEBUG, "AppLifecycleIntegration removed.");
            }
        }
    }
}
